/**
 * (C) Copyright 2009, by Gaixie.org and Contributors.
 * 
 * Project Info:  http://jingsy.gaixie.org/
 *
 * JingsY is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * JingsY is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with JingsY.  If not, see <http://www.gnu.org/licenses/>.
 *
 */

package org.gaixie.jingsy.mysql;

import java.io.File;  
import java.io.FileOutputStream;
import java.io.FileWriter;
import java.io.InputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.io.StringWriter;
import java.util.Date;
import java.util.Enumeration;
import java.util.Iterator;
import java.util.List;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;

import javax.xml.transform.Transformer; 
import javax.xml.transform.TransformerException; 
import javax.xml.transform.TransformerFactory;  
import javax.xml.transform.stream.StreamSource; 

import org.dom4j.Attribute;
import org.dom4j.Document;
import org.dom4j.DocumentException;  
import org.dom4j.DocumentHelper;
import org.dom4j.Element;
import org.dom4j.Node;
import org.dom4j.io.DocumentResult;  
import org.dom4j.io.DocumentSource; 
import org.dom4j.io.HTMLWriter;
import org.dom4j.io.OutputFormat;
import org.dom4j.io.SAXReader; 
import org.dom4j.io.XMLWriter;

/**
 * 
 */
public class Mwb2Html {
    static final int BUFFER = 2048;
    public void parse(String file) throws DocumentException,IOException,TransformerException {  
	ZipEntry entry;
	ZipFile zipfile = new ZipFile(file);
	Enumeration e = zipfile.entries();
	while(e.hasMoreElements()) {
            entry = (ZipEntry) e.nextElement();
            System.out.println("Extracting: " +entry);
	    SAXReader reader = new SAXReader();  
	    Document document = reader.read(zipfile.getInputStream(entry));  
	    data(document);
	}
    }  
    
    public void data(Document document) throws IOException,TransformerException {
        // create new xml document to generate html file.                                                                               
        Document summaryDoc = DocumentHelper.createDocument();
        Element summaryRoot = summaryDoc.addElement("project");

	schemaList(document);
	tableList(document);

	// set project info to new document root
	project(summaryRoot,document);
	Element schema = summaryRoot.addElement("schema");
	// all tables of project
	List list = document.selectNodes("//value[@struct-name='db.mysql.Table']");
	for (Iterator iter = list.iterator(); iter.hasNext(); ) {
	    Element tableNode = (Element) iter.next();
	    table(tableNode, schema, document);
        }

	// create directory : project name
        Document transDoc = styleDocument(summaryDoc,"/summary.xsl");
	write(transDoc,"output/summary.html");
        copyFile("/stylesheet.css","output/stylesheet.css");
        copyFile("/index.html","output/index.html");
    }

    public void project(Element root, Document document) {
	String xpath = "/data/value/value[@struct-name='app.DocumentInfo']";
	Element node = (Element) document.selectSingleNode(xpath);
        for (Iterator i = node.elementIterator(); i.hasNext();) {
            Element element = (Element) i.next();
            root.addAttribute(element.attributeValue("key")
			      , element.getText());
	}
    }

    public void schemaList(Document document) throws IOException,TransformerException {
	Document schemaDoc = DocumentHelper.createDocument();
	Element root = schemaDoc.addElement("project");

	project(root,document);
	String xpath = "//value[@struct-name='db.mysql.Schema']/value[@key='name']";
	List list = document.selectNodes(xpath);       
        for (Iterator i = list.iterator(); i.hasNext(); ) {
            Element element = (Element) i.next();
            root.addElement("schema")
                .addAttribute("name",element.getText());
            new File("output/"+element.getText()).mkdirs();
        }

        // create directory : project name
        Document tranDoc = styleDocument(schemaDoc,"/schemaList.xsl");
        write(tranDoc,"output/schemaList.html");
    }

    public void tableList(Document document) throws IOException,TransformerException {
        Document allTableDoc = DocumentHelper.createDocument();
	Element aroot = allTableDoc.addElement("project");
	Element schema = aroot.addElement("schema").addAttribute("name","All");
	
	String xpath = "//value[@struct-name='db.mysql.Schema']";
        List list = document.selectNodes(xpath);
        for (Iterator i = list.iterator(); i.hasNext(); ) {
            Element element = (Element) i.next();

	    Document tableDoc = DocumentHelper.createDocument();

	    xpath = element.getUniquePath()+"/value[@key='name']";
	    Node n = document.selectSingleNode(xpath);
	    Element root = tableDoc.addElement("project")
		.addElement("schema").addAttribute("name",n.getText());
	    xpath = element.getUniquePath()
		+"/value/value[@struct-name='db.mysql.Table']/value[@key='name']";
	    List l = document.selectNodes(xpath);
	    for (Iterator iter = l.iterator(); iter.hasNext(); ) {
		Element tableNode = (Element) iter.next();
		root.addElement("table")
		    .addAttribute("name",tableNode.getText())
		    .addAttribute("schema",n.getText());
		schema.addElement("table")
		    .addAttribute("name",tableNode.getText())
		    .addAttribute("schema",n.getText());
	    }
	    // create directory : schema name
            new File("output/"+n.getText()).mkdirs();
	    Document tranDoc = styleDocument(tableDoc,"/tableList.xsl");
	    write(tranDoc,"output/"+n.getText()+"/tableList.html");
        }

	Document tranDoc = styleDocument(allTableDoc,"/tableList.xsl");
	write(tranDoc,"output/allTables.html");
    }

    public void table(Element tableNode,Element summary,Document doc) 
	throws IOException,TransformerException {
	Document tdoc = DocumentHelper.createDocument();
        Element tab = tdoc.addElement("project")
	    .addElement("schema")
	    .addElement("table");
        Element stab = summary.addElement("table");
	String  xpath = tableNode.getUniquePath()+"/value[@key='name']";

	Node n = doc.selectSingleNode(xpath);
        tab.addAttribute("name" ,n.getText());
	stab.addAttribute("name" ,n.getText());

	xpath = tableNode.getUniquePath()+"/value[@key='comment']";
        Node c = doc.selectSingleNode(xpath);
        tab.addAttribute("comment" ,c.getText());
        stab.addAttribute("comment",c.getText());
	xpath = tableNode.getUniquePath()+"/value[@key='lastChangeDate']";
        Node d = doc.selectSingleNode(xpath);
        tab.addAttribute("lastChangeDate" ,d.getText());
        stab.addAttribute("lastChangeDate" ,d.getText());

	xpath = tableNode.getUniquePath()+"/value[@key='columns']";
	Element cols = (Element)doc.selectSingleNode(xpath);
	for ( Iterator i = cols.elementIterator(); i.hasNext(); ) {
	    Element columnNode = (Element) i.next();
	    column(columnNode,tab,doc);
	}

	xpath = tableNode.getUniquePath()+"/value[@key='indices']";
	Element inds =(Element)doc.selectSingleNode(xpath);
	for ( Iterator i = inds.elementIterator(); i.hasNext(); ) {
	    Element indexNode = (Element) i.next();
	    index(indexNode,tab,doc);
	}

	xpath = tableNode.getUniquePath()+"/value[@key='foreignKeys']";
        Element fks =(Element)doc.selectSingleNode(xpath);
	for ( Iterator i = fks.elementIterator(); i.hasNext(); ) {
	    Element fkNode = (Element) i.next();
	    foreignKey(fkNode,tab,doc);
	}

	xpath = tableNode.getUniquePath()+"/ancestor::value/value[@key='name']";
        Node sc = doc.selectSingleNode(xpath);
	stab.addAttribute("schema" ,sc.getText());

	String path = "output/"+sc.getText()+"/tables/";
	new File(path).mkdirs();
        Document transDoc = styleDocument(tdoc,"/table.xsl");
        write(transDoc,path+tab.attributeValue("name")+".html");
    }

    public void column(Element columnNode,Element tableRoot,Document document) {
	Element columnRoot = tableRoot.addElement("column");
	for (Iterator i = columnNode.elementIterator(); i.hasNext();) {
            Element element = (Element) i.next();
	    columnRoot.addAttribute(element.attributeValue("key")
				    , element.getText());
	}
	// assembly dataType like int(11) etc.
	String length = columnRoot.attributeValue("length");
	String precision = columnRoot.attributeValue("precision");
	String dataType = columnRoot.attributeValue("simpleType");
	dataType = dataType.substring(dataType.lastIndexOf(".")+1);
	if (!"-1".equals(length)) {
	    dataType = dataType+"("+length+")";
	} else if (!"-1".equals(precision)) {
	    dataType =dataType+"("+precision+")";
	}
	columnRoot.addAttribute("dataType", dataType);

    }

    public void index(Element indexNode, Element indexRoot, Document doc) {
        Element index = indexRoot.addElement("index");
	String xpath = indexNode.getUniquePath()+"/value[@key='name']";
	Node n = doc.selectSingleNode(xpath);
	index.addAttribute("name" ,n.getText());

	xpath = indexNode.getUniquePath()+"/value[@key='indexType']";
        Node t = doc.selectSingleNode(xpath);
        index.addAttribute("indexType" ,t.getText());

	xpath = indexNode.getUniquePath()+"/value[@key='comment']";
        Node c = doc.selectSingleNode(xpath);
	index.addElement("comment").addText(c.getText());

	xpath = indexNode.getUniquePath()
	    +"/value/value/link[@key='referencedColumn']";
        List list = doc.selectNodes(xpath);
        for (Iterator iter = list.iterator(); iter.hasNext(); ) {
            Element e = (Element) iter.next();
	    xpath = "//*[@id='"+e.getText()+"']/value[@key='name']";
	    Node field = doc.selectSingleNode(xpath);

	    if (iter.hasNext()) {
		index.addElement("field").addText(field.getText()+" , ");
	    } else {
		index.addElement("field").addText(field.getText());
	    }
        }

    }

    public void foreignKey(Element fkNode, Element indexRoot, Document doc) {
	Element fk = indexRoot.addElement("foreignKey");
	String xpath = fkNode.getUniquePath()+"/value[@key='name']";
	// foreignKey name
        Node t = doc.selectSingleNode(xpath);
	fk.addAttribute("name" ,t.getText());

	xpath = fkNode.getUniquePath()+"/value[@key='columns']/link";
        List cList = doc.selectNodes(xpath);
        for (Iterator i = cList.iterator(); i.hasNext(); ) {
            Element node = (Element) i.next();
	    // get finally path of foreignKey
	    xpath =  "//*[@id='"+node.getText()+"']/value[@key='name']";
            Node field = doc.selectSingleNode(xpath);

            if (i.hasNext()) {
                fk.addElement("field").addText(field.getText()+" , ");
            } else {
                fk.addElement("field").addText(field.getText());
            }
        }
	// get reference table link id
	xpath = fkNode.getUniquePath()+"/link[@key='referencedTable']";
	t = doc.selectSingleNode(xpath);
	// get reference table node
	xpath = "//*[@id='"+t.getText()+"']/value[@key='name']";
	t = doc.selectSingleNode(xpath);

	xpath = fkNode.getUniquePath()+"/value[@key='referencedColumns']/link";
	List refList = doc.selectNodes(xpath);
	for (Iterator i = refList.iterator(); i.hasNext(); ) {
	    Element node = (Element) i.next();
	    xpath = "//*[@id='"+node.getText()+"']/value[@key='name']";
	    Node field = doc.selectSingleNode(xpath);

	    // last node?
            if (i.hasNext()) {
		// muti ref fields need "," like userbase.user_id,
                fk.addElement("refField").addText(t.getText()+"."+field.getText()+" , ")
		    .addAttribute("refTable" ,t.getText());
            } else {
		// singer ref field like userbase.user_id
                fk.addElement("refField").addText(t.getText()+"."+field.getText())
		    .addAttribute("refTable" ,t.getText());
            }
        }
	xpath = fkNode.getUniquePath()+"/value[@key='comment']";
        t = doc.selectSingleNode(xpath);
        fk.addElement("comment").addText(t.getText());
    }

    public void write(Document document,String outFile) throws IOException{
        // lets write to a file
	// dont use new HTMLWriter(new FileWriter(outFile)), otherwise will occur encoding error
        HTMLWriter writer = new HTMLWriter(new FileOutputStream(outFile));
        writer.write(document);
        writer.close();
    }

    public Document styleDocument( Document document, String stylesheet )
	throws TransformerException {
	InputStream is=this.getClass().getResourceAsStream(stylesheet);

        // load the transformer using JAXP
        TransformerFactory factory = TransformerFactory.newInstance();
        Transformer transformer = factory.newTransformer(new StreamSource(is));

        // now lets style the given document
        DocumentSource source = new DocumentSource(document);
        DocumentResult result = new DocumentResult();
        transformer.transform(source, result);

        // return the transformed document
        Document transformedDoc = result.getDocument();
        return transformedDoc;
    }

    public void copyFile(String oldFile, String newFile) throws IOException {
        InputStream in=this.getClass().getResourceAsStream(oldFile);
	File f=new File(newFile);
	OutputStream out=new FileOutputStream(f);
	byte buf[]=new byte[1024];
	int len;
	while((len=in.read(buf))>0) {
	    out.write(buf,0,len);
	}
	out.close();
	in.close();
    }

}
